<?php

namespace App\Api\Exceptions;

use App\Exceptions\OtherException;
use App\Exceptions\BaseException;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that should not be reported.
     *
     * @var array
     */
    protected $dontReport = [
        \Illuminate\Auth\AuthenticationException::class,
        \Illuminate\Auth\Access\AuthorizationException::class,
        \Symfony\Component\HttpKernel\Exception\HttpException::class,
        \Illuminate\Database\Eloquent\ModelNotFoundException::class,
        \Illuminate\Session\TokenMismatchException::class,
        \Illuminate\Validation\ValidationException::class,
        \InvalidArgumentException::class,
        BaseException::class,
    ];

    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  \Exception $exception
     *
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request                              $request
     * @param  \Exception|\Illuminate\Validation\ValidationException $exception
     *
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        if ($this->isValidationException($exception)) {
            $error_msg = $exception->validator->errors()->first();
            $exception = new OtherException($error_msg, BaseException::SYSTEM_VALIDATE_ERROR_DEFAULT_CODE);
        }

        if (($base = $this->baseException($request, $exception)) != '') {
            return $base;
        }

        return parent::render($request, $exception);
    }

    /**
     * Render an biz exception into an HTTP response.
     *
     * @param           $request
     * @param Exception $exception
     *
     * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
     */
    protected function baseException($request, Exception $exception)
    {
        $data = [
            'code'    => BaseException::SYSTEM_ERROR_DEFAULT_CODE,
            'message' => BaseException::SYSTEM_ERROR_DEFAULT_MESSAGE,
        ];
        $http_code = 500;
        if ($exception instanceof BaseException) {
            $data = [
                'code'    => $exception->getCode() ?: 9999,
                'message' => $exception->getMessage(),
            ];
            $http_code = 400;
        } elseif ($exception instanceof \InvalidArgumentException) {
            $data['message'] = $exception->getMessage();
            $http_code = 400;
        } elseif ($exception instanceof MethodNotAllowedHttpException) {
            $http_code = 404;
        } elseif ($this->isHttpException($exception)) {
            $http_code = $exception->getStatusCode();
        }

        if ($http_code == 404) {
            return response('Not Found', $http_code);
        }

        if (!app()->environment('production')) {
            $data['context'] = get_class($exception) . ' :: ' . $exception->getMessage() . ' in ' . $exception->getFile() . ' +' . $exception->getLine();
            $data['trace'] = explode("\n", $exception->getTraceAsString());
        }

        return response()->json($data, $http_code);
    }

    /**
     * Convert an authentication exception into an unauthenticated response.
     *
     * @param  \Illuminate\Http\Request                 $request
     * @param  \Illuminate\Auth\AuthenticationException $exception
     *
     * @return \Illuminate\Http\Response
     */
    protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }

        return redirect()->guest(route('login'));
    }

    /**
     * Determine if the given exception is an validate exception.
     *
     * @param  \Exception $e
     *
     * @return bool
     */
    protected function isValidationException(Exception $e)
    {
        return $e instanceof \Illuminate\Validation\ValidationException;
    }
}
